import time
import sys
import random
from collections import Counter
from gui import *
from crypto_rules import CryptoScoring, CryptoWinConditions
from player import CryptoScholar  # 导入新的密码学学者类


class Game:
    def __init__(self, gui):
        self.players = gui.players
        self.current_player_index = 0  # 当前玩家的索引
        self.wall = Table()  # 使用 Table 类来生成牌墙
        self.discard_pile = []  # 打出来的牌，可能是别"组合""复制"在side中，也可能在river里，总之是"明牌"。为后面ai判断出牌准备
        self.dealer = None  # 庄家属性
        self.winner = None
        self.winning_tile = None  # "胡"那张牌
        self.remaining_tiles = 138  # 码将总牌数 (9*4)*3 + 8*4 + 2 = 138
        self.is_game_over = False
        self.gui = gui
        self.circle = 1  # 局数
        self.mouse_clicked = False  # 轮到人类玩家出牌，设置允许鼠标点击
        self.win_type = None  # 表示赢牌的类型：自摸还是点炮
        self.turn_count = 0  # 回合计数器，用于知识分享
        
        # 码将专用规则引擎
        self.crypto_scoring = CryptoScoring()
        self.crypto_win_conditions = CryptoWinConditions()
        self.quantum_tiles_in_play = []  # 记录场上的量子计算机牌

    def current_player(self):
        return self.players[self.current_player_index]

    def determine_dealer(self):
        # 简化庄家逻辑：始终从第一个玩家开始
        self.dealer = self.players[0]
        self.current_player_index = 0
        
        msg = f"起始玩家是 {self.dealer.name}"
        print(msg)

    def start_game(self):
        # 初始化牌墙
        self.init_wall()
        # 随机选择庄家
        self.determine_dealer()
        # 设置庄家为当前玩家
        self.current_player_index = self.players.index(self.dealer)
        # 庄家抓牌
        self.deal_tiles(self.current_player(), 14)
        # 其余玩家抓牌
        for player in self.players:
            if player != self.current_player():
                self.deal_tiles(player, 13)
        # 对每个玩家的手牌进行排序
        for player in self.players:
            player.sort_hand()
        # 打印游戏状态
        self.print_game_state()

    def determine_and_display_discarded_tile(self):
        # 检查是否有玩家破解
        if self.winner:
            return
        # 检查牌墙是否为空
        if self.wall.remaining_tiles() == 0:
            print("牌墙已空，流局！")
            # 这里可以添加流局的处理逻辑，比如统计各个玩家的得分情况等
            self.circle += 1
            self.is_game_over = True  # 触发While True 循环中将判断执行reset_game()
            return

        current_player = self.current_player()
        
        # ===== 移除特殊胜利条件检查，改为正常胡牌时的加分项 =====

        # 如果玩家已经分析，则不考虑强化，只考虑破解和打出这张牌
        if current_player.riichi_flag:
            # 如果当前玩家已经分析，则判断抓到的牌是否是分析的牌
            if current_player.drawn_tile.text in current_player.riichi_list:
                # 如果抓到的牌是分析的牌，则判定破解
                self.win_type = 'self_drawn'
                current_player.hand.remove(current_player.drawn_tile)  # 避免show_scores时 显示多一张牌。因为自摸时这张胡牌已经加入到手牌。
                # current_player.hand.pop()  # 移除手牌中的最后一个成员，也就是刚抓上来的那张牌，这样避免对倒时移除掉中间的同张牌。
                self.handle_win(current_player.drawn_tile, current_player)
            else:
                # 如果抓到的牌不是听的牌，则直接打出这张牌
                self.discard_tile(current_player, current_player.drawn_tile)
            return  # 返回，避免执行kong后再remove掉tile时报错

        if isinstance(current_player, Bot):
            # 如果是 Bot 类玩家，AI算法打牌
            tile_to_discard = current_player.make_decision(self.discard_pile)
            # 测试流局，随机出牌
            # tile_to_discard = random.choice(current_player.hand)
            self.discard_tile(current_player, tile_to_discard)
        elif hasattr(current_player, 'make_decision'):
            # 如果是 CryptoScholar 类玩家，AI算法打牌并可能分享知识
            tile_to_discard = current_player.make_decision(self.discard_pile, self.turn_count)
            
            # 检查是否需要分享知识（修复逻辑）
            if (hasattr(current_player, 'should_share_knowledge') and 
                hasattr(current_player, 'last_share_turn') and
                current_player.should_share_knowledge(self.turn_count, tile_to_discard)):
                knowledge = current_player.share_knowledge()
                # 传递学者照片路径
                photo_path = getattr(current_player, 'photo_path', None)
                self.gui.display_scholar_knowledge(current_player.name, knowledge, photo_path)
                current_player.last_share_turn = self.turn_count
            
            self.discard_tile(current_player, tile_to_discard)
        else:
            # 如果是人类玩家，由MouseButtonDown事件处理
            # self.gui.render_player_hand(current_player)
            self.mouse_clicked = True

    def next_turn(self):
        self.current_player_index = (self.current_player_index + 1) % len(self.players)
        self.turn_count += 1  # 增加回合计数
        print(f"当前玩家索引: {self.current_player_index}，回合: {self.turn_count}")

    def draw_end(self):
        # 在游戏结束且流局时，渲染流局图片
        if self.is_game_over and self.remaining_tiles == 0:
            # 调用 render_game_state 方法，渲染当前游戏状态
            self.gui.render_game_state(self.dealer, self.is_game_over)

            # 加载流局图片
            draw_pic = pygame.image.load( resource_path(os.path.join("pic", "end.png"))).convert_alpha()
            # 获取屏幕中心坐标
            screen_center = (self.gui.screen.get_width() // 2, self.gui.screen.get_height() // 2)
            # 获取流局图片的位置
            draw_rect = draw_pic.get_rect(center=screen_center)
            # 在屏幕中心绘制流局图片
            self.gui.screen.blit(draw_pic, draw_rect)

            self.gui.display_message("牌墙已空，无人破解！")
            # 输出文本 加上之后"重新开始"按钮不停地闪烁
            # self.gui.draw_remaining_tiles(self.wall.remaining_tiles(), self.circle)

    def draw_tile(self):
        # 摸牌
        current_player = self.current_player()
        tile = self.wall.pop()
        current_player.hand.append(tile)
        current_player.drawn_tile = tile

        print(f"{current_player.name} 摸了一张牌: {tile.text}")
        if len(current_player.riichi_list) > 0:
            print(f"{current_player.name} 分析: {current_player.riichi_list}")

        # 更新剩余牌数
        self.remaining_tiles -= 1

        # ===== 高级机制检查 =====
        # 记录量子牌
        if tile.kind == 'T' and tile.n == 8:  # 量子计算机是T(8)
            self.quantum_tiles_in_play.append(tile)
            print(f"量子计算机牌出现！当前场上量子牌数量: {len(self.quantum_tiles_in_play)}")

    

        # 检查兼容性问题
        self.check_compatibility_issues(current_player)

        # 检查是否摸到了强化牌，如果已经分析不能再强化，否则破坏了现有牌型导致分析失效
        if not current_player.riichi_flag:
            if current_player.hand.count(tile) == 4 or current_player.side.count(tile) == 3:
                # 检查是否摸到了强化牌
                self.handle_reinforce(current_player, tile)
                self.draw_tile()  # 如果强化了算法，继续摸一张牌

    def init_wall(self):
        # 初始化麻将牌墙，一共 136 张牌，包括万、条、筒和字牌
        self.wall.generate_tiles()

    def deal_tiles(self, player, count):
        # 给玩家发牌
        for _ in range(count):
            tile = self.wall.pop()
            self.remaining_tiles -= 1
            player.hand.append(tile)

    def discard_tile(self, player, tile):
        if tile in player.hand:
            player.hand.remove(tile)
            self.discard_pile.append(tile)
            msg = f"{player.name} 打出了 {tile.text}"
            # 显示信息
            self.gui.display_message(msg)
            # 打印调试信息
            print(msg)
            # 在屏幕中央显示这张牌
            self.gui.display_discarded_tile(tile)
            self.gui.draw_remaining_tiles(self.wall.remaining_tiles(),self.circle)
            time.sleep(1)  # 影响人类玩家出牌时间和鼠标点击
            self.handle_discard(player, tile)
        else:
            # 添加调试输出
            print(f"Error: Tried to remove tile {tile} that is not in player {player.name}'s hand")
            print(f"Player {player.name}'s hand: {player.hand}")
            print(f"Tile to remove: {tile}")
            raise ValueError(f"Cannot discard tile {tile}: not in hand")

    def handle_discard(self, discard_player, tile):
        """处理玩家打出一张牌后的组合复制强化破解操作判断"""
        # ===== 量子攻击机制 =====
        if tile.kind == 'T' and tile.n == 8:  # 量子计算机是T(8)
            # 量子计算机被打出，检查是否触发量子攻击
            for target_player in self.players:
                if target_player != discard_player:
                    attack_success = self.apply_quantum_attack(discard_player, target_player, tile)
                    if attack_success:
                        break  # 只攻击一个目标

        # 检查是否有玩家可以破解
        for other_player in self.players:
            if other_player != discard_player and other_player.check_win(tile):
                self.win_type = 'discarded'
                self.handle_win(tile, other_player, discard_player)
                return

        # 检查当前玩家是否可以宣告分析
        if discard_player.check_riichi():
            discard_player.riichi_flag = True

        discard_player.drawn_tile = None  # drawn_tile如果不重置，下次发生"组合""复制"时，会再次把上次的drawn_tile牌移到最后

        # 检查其他玩家是否可以复制算法
        for other_player in self.players:
            if other_player != discard_player and not other_player.riichi_flag:
                if other_player.check_duplicate(tile):  # 检查是否可以复制算法
                    if isinstance(other_player, Player) and other_player.position == "南":
                        # 如果是人类玩家，则显示按钮并等待点击
                        mouse_pos = pygame.mouse.get_pos()
                        self.gui.action_buttons["复制"].draw(self.gui.screen, mouse_pos)
                        self.gui.action_buttons["取消"].draw(self.gui.screen, mouse_pos)
                        pygame.display.flip()
                        # 等待玩家点击按钮
                        waiting_for_click = True
                        while waiting_for_click:  # 使用布尔变量控制循环
                            for event in pygame.event.get():
                                if event.type == pygame.MOUSEBUTTONDOWN:
                                    mouse_pos = pygame.mouse.get_pos()
                                    if self.gui.action_buttons["复制"].rect.collidepoint(mouse_pos):  # 标记修改点
                                        self.handle_duplicate(other_player, tile)  # 处理复制算法操作
                                        return
                                    elif self.gui.action_buttons["取消"].rect.collidepoint(mouse_pos):  # 标记修改点
                                        waiting_for_click = False  # 设置为 False 以退出循环
                                        break  # 取消组合，直接进入下一轮
                    else:
                        # 如果是 AI 玩家，则直接处理复制算法操作
                        self.handle_duplicate(other_player, tile)  # 处理复制算法操作
                        return

                # 检查是否可以强化算法
                if other_player.check_reinforce(tile):
                    self.handle_reinforce(other_player, tile)  # 处理强化算法操作
                    self.draw_tile()
                    return

        # 检查其他玩家是否可以组合算法
        # "组合"的优先级比较低, 而且只能是下家组合
        next_player_index = (self.current_player_index + 1) % len(self.players)
        next_player = self.players[next_player_index]
        if not next_player.riichi_flag and next_player.check_combine(tile):
            combine_combinations = next_player.get_combine_combinations(tile)
            # 即便Player是Bot玩家，这行表达式会返回 True，因为Bot类是从Player类继承而来的，
            # 所以Bot的实例也是Player类的实例（所以必须加条件"南"）或者给Player加一个属性：self.type = "human" / "bot"
            if isinstance(next_player, Player) and next_player.position == "南":  # 是人类玩家
                if len(combine_combinations) == 1:
                    # 如果是人类玩家，则显示按钮并等待点击
                    mouse_pos = pygame.mouse.get_pos()
                    self.gui.action_buttons["组合"].draw(self.gui.screen, mouse_pos)
                    self.gui.action_buttons["取消"].draw(self.gui.screen, mouse_pos)
                    pygame.display.flip()
                    # 等待玩家点击按钮
                    waiting_for_click = True
                    while waiting_for_click:  # 使用布尔变量控制循环
                        for event in pygame.event.get():
                            if event.type == pygame.MOUSEBUTTONDOWN:
                                mouse_pos = pygame.mouse.get_pos()
                                if self.gui.action_buttons["组合"].rect.collidepoint(mouse_pos):  # 标记修改点
                                    self.handle_combine(next_player, tile, combine_combinations[0])  # 标记修改点
                                    return
                                elif self.gui.action_buttons["取消"].rect.collidepoint(mouse_pos):  # 标记修改点
                                    waiting_for_click = False  # 设置为 False 以退出循环
                                    break  # 取消组合，直接进入下一轮
                else:
                    combination_rects = self.show_combine_combinations(next_player, tile)
                    if combination_rects:
                        self.gui.action_buttons["取消"].draw(self.gui.screen)
                        pygame.display.flip()
                        waiting_for_click = True
                        selected_combination = None
                        while waiting_for_click:
                            for event in pygame.event.get():
                                if event.type == pygame.MOUSEBUTTONDOWN:
                                    mouse_pos = pygame.mouse.get_pos()
                                    for rect, combination in combination_rects:
                                        if rect.collidepoint(mouse_pos):  # 检查点击的是否为某个组合
                                            selected_combination = combination
                                            break
                                    if selected_combination:
                                        self.handle_combine(next_player, tile, selected_combination)  # 处理组合并传入选择的组合
                                        return
                                    elif self.gui.action_buttons["取消"].rect.collidepoint(mouse_pos):
                                        waiting_for_click = False  # 取消组合，直接进入下一轮
                                        break
            else:
                # 如果是 AI 玩家，则直接处理组合操作
                self.handle_combine(next_player, tile, combine_combinations[0])
                return

        # 如果没有任何玩家组合复制强化破解，则进入下一个玩家的回合
        discard_player.river.append(tile)
        self.next_turn()
        self.draw_tile()

    def show_combine_combinations(self, player, tile):
        """显示算法组合"""
        combine_combinations = player.get_combine_combinations(tile)
        if len(combine_combinations) > 1:
            combination_rects = []

            screen_width = self.gui.screen.get_width()
            tile_width = TILE_WIDTH  # 使用新的牌宽度
            total_width = (tile_width * 3 + 20) * len(combine_combinations) - 20  # 增加组合之间的间距

            x_start = (screen_width - total_width) // 2

            # 显示所有算法组合
            for i, combination in enumerate(combine_combinations):
                x_offset = x_start + (tile_width * 3 + 20) * i
                for j, t in enumerate(combination):
                    position = (x_offset + j * (tile_width + TILE_SPACING), 500)  # 调整Y轴位置并添加间距
                    self.gui.show_tile(t, position)
                    if j == 0:
                        rect = pygame.Rect(position[0], position[1], tile_width * 3 + TILE_SPACING * 2, TILE_HEIGHT)
                        combination_rects.append((rect, combination))

            mouse_pos = pygame.mouse.get_pos()
            self.gui.action_buttons["取消"].draw(self.gui.screen, mouse_pos)
            pygame.display.flip()

            return combination_rects

        return []

    def play_sound(self, sound_file):
        """播放音频文件"""
        # 已移除音效功能
        pass

    def handle_duplicate(self, duplicate_player, tile):
        """处理复制算法操作"""
        duplicate_player.duplicate(tile)
        self.current_player_index = self.players.index(duplicate_player)

    def handle_reinforce(self, reinforce_player, tile):
        """处理强化算法操作"""
        # 正常"复制"强化
        # if reinforce_player.hand.count(tile) == 3:
        reinforce_player.reinforce(tile)
        self.current_player_index = self.players.index(reinforce_player)

    def handle_combine(self, combine_player, tile, combination):
        """处理算法组合操作"""
        # 调用Player类的combine方法，传递具体的组合
        combine_player.combine(tile, combination)
        # 进行下一轮操作
        self.next_turn()

    def handle_win(self, tile, win_player, discard_player=None):
        """处理破解操作"""
        self.winner = win_player
        # self.winner.hand.append(tile) # 破解牌加入手牌，以正确计算番数，否则强化算法缺失
        self.is_game_over = True  # 触发While True 循环中将判断执行reset_game()
        self.gui.score_button_drawn = True  # 显示统计分数按钮

        if discard_player:
            msg = f"{discard_player.name} 放炮 {tile.text}，{win_player.name} 破解！"
        else:
            msg = f"{win_player.name} 自摸 {tile.text} 破解！"

        self.winning_tile = tile
        self.gui.render_game_state(self.dealer, self.is_game_over)  # 赢了，明牌显示player手牌，以确认胡牌牌型

        self.gui.display_message(msg, 'top')
        print(msg)
        self.print_game_state()  # 此时game_over == true, 没有输出剩余牌信息
        # 计算分数
        fan, messages = self.calculate_fan(win_player)
        self.calculate_score(win_player, discard_player, fan, self.win_type)

    def is_ponponhu(self, tiles):
        """检查是否有4个刻子"""
        # 统计每个牌的数量
        tile_counts = Counter(tiles)
        triplet_count = 0
        pair_count = 0

        # 统计每种牌的数量
        for count in tile_counts.values():
            if count == 3:
                triplet_count += 1
            elif count == 2:
                pair_count += 1

        # 如果有4个刻子并且有1个对子
        return triplet_count == 4 and pair_count == 1

    def calculate_fan(self, player):
        messages = [f"第 {self.circle} 局", f"赢家： {self.winner.name} "]

        def add_message(reason, points):
            messages.append(f"{reason}  {'加' if points > 0 else '扣'} {abs(points)} 番")

        # 获取所有牌（包括胡牌）
        tiles = self.winner.side + self.winner.hand
        if self.winning_tile is not None:
            tiles.append(self.winning_tile)

        # 计算赢牌玩家的番数
        fan = 0

        # ===== 基础麻将规则 =====
        # 移除庄家相关的加分规则
        # if self.winner == self.dealer:
        #     fan += 1
        #     add_message("庄家赢", 1)

        # if self.dealer.fangchong:
        #     fan += 1
        #     add_message("庄点炮", 1)

        if len(self.winner.riichi_list) == 1:
            fan += 2
            add_message("胡单张", 2)
        else:
            fan += 1
            add_message("平胡", 1)

        if len(self.winner.hand) == 1:
            fan += 1
            add_message("手把一", 1)

        if len(self.winner.side) == 0:
            fan += 1
            add_message("门前清", 1)

        # 强化算法奖励
        counted_tiles = set()
        for tile in tiles:
            if tile not in counted_tiles and tiles.count(tile) == 4:
                fan += 1
                add_message("强化算法", 1)
                counted_tiles.add(tile)

        # 全复制系统
        if self.is_ponponhu(tiles):
            fan += 3
            add_message("全复制系统", 3)

        # ===== 密码学专精规则 =====
        kinds = {tile.kind for tile in tiles if tile is not None}
        if len(kinds) == 1:
            if list(kinds)[0] == 'S':
                fan += 6
                add_message("对称加密专精", 6)
            elif list(kinds)[0] == 'A':
                fan += 7
                add_message("非对称加密专精", 7)
            elif list(kinds)[0] == 'H':
                fan += 5
                add_message("哈希函数专精", 5)
            else:
                fan += 4
                add_message("单一类型", 4)

        # 密码学融合奖励
        crypto_kinds = {tile.kind for tile in tiles if tile is not None and tile.kind in ['S', 'A', 'H']}
        term_kinds = {tile.kind for tile in tiles if tile is not None and tile.kind == 'T'}
        if len(crypto_kinds) >= 2 and len(term_kinds) > 0:
            fan += 4
            add_message("密码学融合", 4)

        # ===== 完整密码学高分组合 =====
        crypto_bonus = self.crypto_scoring.calculate_crypto_bonus(
            tiles, 
            self.quantum_tiles_in_play
        )
        
        # 详细分解高分组合
        special_combos = self.get_special_combo_details(tiles)
        for combo_name, combo_score in special_combos:
            if combo_score != 0:
                add_message(combo_name, combo_score)
                fan += combo_score

        # ===== 高级游戏机制检查 =====
        # 兼容性检查
        compatibility_ok, compatibility_msg = self.crypto_scoring.check_compatibility(tiles)
        if not compatibility_ok:
            fan -= 2
            add_message(f"兼容性问题: {compatibility_msg}", -2)


        # 量子防御成功奖励
        if self.check_quantum_defense_success(tiles):
            fan += 3
            add_message("量子防御成功", 3)

        # ===== 特殊密码学系统奖励（原特殊胜利条件改为加分项） =====
        special_system_bonus = self.check_special_systems_bonus(tiles)
        for system_name, bonus_points in special_system_bonus:
            fan += bonus_points
            add_message(system_name, bonus_points)

        return fan, messages

    def get_special_combo_details(self, tiles):
        """获取特殊组合的详细得分"""
        combos = []
        
        # 完整加密流程 (8分)
        if self.crypto_scoring.check_complete_encryption(tiles) > 0:
            combos.append(("完整加密流程", 8))
        
        # 数字签名系统 (10分)
        if self.crypto_scoring.check_digital_signature(tiles) > 0:
            combos.append(("数字签名系统", 10))
        
        # 哈希验证系统 (6分)
        if self.crypto_scoring.check_hash_verification(tiles) > 0:
            combos.append(("哈希验证系统", 6))
        
        # 密钥交换协议 (12分)
        if self.crypto_scoring.check_key_exchange(tiles) > 0:
            combos.append(("密钥交换协议", 12))
        
        # 抗量子加密 (15分)
        post_quantum_score = self.crypto_scoring.check_post_quantum(tiles)
        if post_quantum_score > 0:
            combos.append(("抗量子加密", post_quantum_score))
        
        # 量子攻击检查（负分）
        attack_score = self.crypto_scoring.check_quantum_attack(tiles, self.quantum_tiles_in_play)
        if attack_score < 0:
            combos.append(("量子攻击", attack_score))
        
        return combos

    def check_timeline_effects(self, tiles):
        """检查时间线机制效果"""
        bonus = 0
        
        # 8-9级算法可以替换1-3级算法
        for tile in tiles:
            if tile is not None and tile.kind in ['S', 'A', 'H'] and tile.n >= 8:
                # 检查是否有低级算法被淘汰
                low_level_tiles = [t for t in tiles if t is not None and t.kind == tile.kind and t.n <= 3]
                if low_level_tiles:
                    bonus += 2  # 每淘汰一类低级算法加2分
        
        return bonus

    def check_quantum_defense_success(self, tiles):
        """检查是否成功防御量子攻击"""
        # 拥有量子牌且有后量子算法
        has_quantum = any(t is not None and t.kind == 'T' and t.n == 8 for t in tiles)
        has_post_quantum = any(
            t is not None and (
                (t.kind == 'S' and t.n >= 8) or  # 高级对称算法
                (t.kind == 'A' and t.n >= 8) or  # 高级非对称算法  
                (t.kind == 'H' and t.n >= 8)     # 高级哈希算法
            )
            for t in tiles
        )
        
        return has_quantum and has_post_quantum

    def check_special_systems_bonus(self, tiles):
        """检查特殊密码学系统，返回加分项列表"""
        bonus_list = []
        
        # 完整PKI系统 (20分)
        has_certificate = any(t is not None and t.kind == 'T' and t.n == 5 for t in tiles)
        has_signature = any(t is not None and t.kind == 'T' and t.n == 4 for t in tiles)
        has_asymmetric = any(t is not None and t.kind == 'A' for t in tiles)
        has_hash = any(t is not None and t.kind == 'H' for t in tiles)
        
        if has_certificate and has_signature and has_asymmetric and has_hash:
            bonus_list.append(("完整PKI系统", 20))
        
        # 完美前向保密 (15分)
        has_key_exchange = any(t is not None and t.kind == 'A' and t.n >= 7 for t in tiles)
        has_symmetric = any(t is not None and t.kind == 'S' for t in tiles)
        has_random = any(t is not None and t.kind == 'T' and t.n == 7 for t in tiles)
        has_hash_verify = any(t is not None and t.kind == 'H' for t in tiles)
        
        if has_key_exchange and has_symmetric and has_random and has_hash_verify:
            bonus_list.append(("完美前向保密", 15))
        
        # 抗量子防御系统 (25分)
        post_quantum_count = sum(1 for t in tiles if t is not None and (
            (t.kind == 'S' and t.n >= 8) or  # 高级对称算法
            (t.kind == 'A' and t.n >= 8) or  # 高级非对称算法  
            (t.kind == 'H' and t.n >= 8)     # 高级哈希算法
        ))
        
        if post_quantum_count >= 3:
            bonus_list.append(("抗量子防御系统", 25))
        
        # 三重安全系统 (30分) - 最高奖励
        has_encryption_system = any(t is not None and t.kind == 'S' for t in tiles) and any(t is not None and t.kind == 'T' and t.n == 3 for t in tiles)
        has_signature_system = has_signature and has_asymmetric
        has_verification_system = has_hash and any(t is not None and t.kind == 'T' and t.n == 6 for t in tiles)
        
        if has_encryption_system and has_signature_system and has_verification_system:
            bonus_list.append(("三重安全系统", 30))
        
        # 密码学大师 (10分) - 拥有所有三种密码学类型的高级算法
        has_advanced_symmetric = any(t is not None and t.kind == 'S' and t.n >= 7 for t in tiles)
        has_advanced_asymmetric = any(t is not None and t.kind == 'A' and t.n >= 7 for t in tiles)
        has_advanced_hash = any(t is not None and t.kind == 'H' and t.n >= 7 for t in tiles)
        
        if has_advanced_symmetric and has_advanced_asymmetric and has_advanced_hash:
            bonus_list.append(("密码学大师", 10))
        
        return bonus_list

    def apply_quantum_attack(self, attacker_player, target_player, target_tile):
        """执行量子攻击机制"""
        if target_tile.kind == 'T' and target_tile.n == 8:  # 使用量子计算机攻击
            # 检查目标玩家是否有可被破解的经典算法
            vulnerable_tiles = []
            for tile in target_player.hand + target_player.side:
                if (tile.kind == 'A' and tile.n <= 3) or \
                   (tile.kind == 'H' and tile.n <= 2):  # RSA-1024/2048, MD5/SHA-1
                    vulnerable_tiles.append(tile)
            
            if vulnerable_tiles:
                # 随机选择一个被破解的算法
                broken_tile = random.choice(vulnerable_tiles)
                target_player.hand.remove(broken_tile) if broken_tile in target_player.hand else target_player.side.remove(broken_tile)
                
                msg = f"量子攻击！{target_player.name}的{broken_tile.text}被破解！"
                self.gui.display_message(msg)
                print(msg)
                
                # 攻击者得分，被攻击者扣分
                attacker_player.score.add_points(5)
                target_player.score.subtract_points(10)
                
                return True
        
        return False

    def check_algorithm_obsolescence(self, player, new_tile):
        """检查算法淘汰机制"""
        if new_tile.kind in ['S', 'A', 'H'] and new_tile.n >= 8:
            # 高级算法可以淘汰低级算法
            obsolete_tiles = []
            for tile in player.hand:
                if tile.kind == new_tile.kind and tile.n <= 3:
                    obsolete_tiles.append(tile)
            
            if obsolete_tiles:
                for tile in obsolete_tiles:
                    player.hand.remove(tile)
                    # 只向当前玩家显示具体淘汰信息，避免暴露手牌
                    if isinstance(player, Player) and player.position == "南":  # 人类玩家
                        msg = f"你的{tile.text}被{new_tile.text}淘汰！"
                        self.gui.display_message(msg)
                        print(f"{player.name}的{tile.text}被{new_tile.text}淘汰！")  # 调试信息
                    else:  # AI玩家
                        print(f"{player.name}的{tile.text}被{new_tile.text}淘汰！")  # 仅控制台输出
                
                # 向所有玩家显示通用信息（不暴露具体牌型）
                if len(obsolete_tiles) > 0:
                    general_msg = f"{player.name}因算法升级更新了{len(obsolete_tiles)}张牌"
                    if isinstance(player, Player) and player.position == "南":
                        # 人类玩家已经看到详细信息，不需要重复显示通用信息
                        pass
                    else:
                        # 只对AI玩家的操作显示通用信息给所有人
                        self.gui.display_message(general_msg)
                
                # 强制摸牌补充
                replacement_count = len(obsolete_tiles)
                for _ in range(replacement_count):
                    if self.wall.remaining_tiles() > 0:
                        replacement_tile = self.wall.pop()
                        player.hand.append(replacement_tile)
                        self.remaining_tiles -= 1
                
                return True
        
        return False

    def check_compatibility_issues(self, player):
        """检查兼容性问题"""
        hand_tiles = player.hand + player.side
        
        # MD5 + SHA-1 兼容性问题
        has_md5 = any(t.kind == 'H' and t.n == 1 for t in hand_tiles)
        has_sha1 = any(t.kind == 'H' and t.n == 2 for t in hand_tiles)
        
        if has_md5 and has_sha1:
            # 只向当前玩家显示具体兼容性问题，避免暴露手牌
            if isinstance(player, Player) and player.position == "南":  # 人类玩家
                msg = f"你的MD5和SHA-1存在兼容性冲突！扣除3分"
                self.gui.display_message(msg)
                print(f"{player.name}的MD5和SHA-1存在兼容性冲突！")  # 调试信息
            else:  # AI玩家
                general_msg = f"{player.name}遇到兼容性问题，扣除3分"
                self.gui.display_message(general_msg)
                print(f"{player.name}的MD5和SHA-1存在兼容性冲突！")  # 仅控制台输出
            
            player.score.subtract_points(3)
            return True
        
        return False

    def calculate_score(self, winner, loser, fan, win_type):
        base_score = 2  # 基础分数
        score = base_score * (2 ** fan)
        total_score = 0
        score_msgs = []  # 成绩显示

        if win_type == 'self_drawn':
            # 自摸的情况，所有其他玩家都要支付分数
            for player in self.players:
                if player != winner:
                    # 移除庄家支付双倍的规则，所有玩家支付相同分数
                    player.score.subtract_points(score)
                    winner.score.add_points(score)
                    total_score += score
                    msg = f"{player.name} 失去了 {score} 分"
                    score_msgs.append(msg)

        else:
            # 放铳的情况，放铳玩家支付分数
            loser.score.subtract_points(score)
            winner.score.add_points(score)
            msg = f"{loser.name} 失去了 {score} 分"
            score_msgs.append(msg)
            total_score = score

        msg = f"{winner.name} 赢得了 {total_score} 分"
        score_msgs.append(msg)
        self.gui.display_messages(score_msgs, 'center')

    def print_game_state(self):
        if not self.is_game_over:  # 只在游戏未结束时更新信息
            self.gui.draw_remaining_tiles(self.wall.remaining_tiles(), self.circle)
        # 打印当前游戏状态，包括玩家手牌和弃牌堆
        msg = "当前游戏状态："
        for player in self.players:
            msg += f"\n{player.name} 手牌: {player.hand}"
        msg += f"\n弃牌堆: {self.discard_pile}"
        # self.display_message(msg)
        # print(msg)

    # ===== 原特殊胜利条件已改为加分项，这些方法已不再使用 =====
    # def check_special_victory_conditions(self, player):
    #     """原特殊胜利条件检查 - 已废弃，改为加分项"""
    #     pass
    
    # def check_instant_victory(self, player):
    #     """原即时胜利检查 - 已废弃，改为加分项"""
    #     pass

    def reset_game(self):
        # 游戏重新初始化的逻辑，包括重置所有游戏状态
        self.is_game_over = False  # 重置游戏结束的标志
        self.current_player_index = 0
        self.discard_pile = []
        self.wall = Table()
        # self.dealer = None
        # self.winner = None
        self.winning_tile = None
        self.remaining_tiles = 138
        self.quantum_tiles_in_play = []  # 重置量子牌记录
        self.turn_count = 0  # 重置回合计数
        for player in self.players:
            player.reset()
            # 重置学者的知识分享状态
            if hasattr(player, 'knowledge_shared'):
                player.last_share_turn = -1
        # 重新初始化牌墙等游戏状态
        self.start_game()

    def handle_mouse_button_down(self):
        if self.mouse_clicked:  # 只有当鼠标未点击时才处理
            print('Mouse down')
            mouse_pos = pygame.mouse.get_pos()
            for player in self.players:
                if isinstance(player, Player) and player.position == "南":
                    x, y = 200, 870  # 更新起始位置以匹配新的布局
                    for tile in player.hand:
                        tile_rect = tile.rect.move(x, y)
                        if tile_rect.collidepoint(mouse_pos):
                            self.discard_tile(player, tile)
                            self.mouse_clicked = False
                            return
                        x += TILE_WIDTH + TILE_SPACING  # 添加间距

    def handle_events(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                return False
            elif event.type == pygame.MOUSEWHEEL:
                # 处理滚轮事件（仅在显示规则时）
                if self.gui.showing_rules:
                    scroll_speed = 30  # 滚动速度
                    self.gui.rules_scroll_offset -= event.y * scroll_speed
                    # 限制滚动范围
                    self.gui.rules_scroll_offset = max(0, min(self.gui.rules_scroll_offset, self.gui.max_scroll_offset))
                    # 不直接调用show_rules()，让主循环处理重新渲染
            elif event.type == pygame.MOUSEMOTION:
                if not self.gui.showing_rules and not self.gui.showing_scoreboard:  # 只在非规则和非计分板显示状态下处理鼠标悬停
                    self.gui.handle_mouse_hover(event.pos)
            elif event.type == pygame.MOUSEBUTTONDOWN:
                if self.gui.showing_rules:
                    # 如果正在显示规则，检查点击位置
                    # 只有点击在规则显示区域外才退出（比如点击右边的滚动条区域外）
                    if event.pos[0] < self.gui.screen_width - 50:  # 不在滚动条区域
                        # 检查是否点击在内容区域
                        content_area = pygame.Rect(50, 50, self.gui.screen_width - 100, self.gui.screen_height - 100)
                        if not content_area.collidepoint(event.pos):
                            # 点击在内容区域外，退出规则显示
                            self.gui.showing_rules = False
                            self.gui.rules_scroll_offset = 0  # 重置滚动位置
                    else:
                        # 点击在滚动条区域，也退出规则显示
                        self.gui.showing_rules = False
                        self.gui.rules_scroll_offset = 0  # 重置滚动位置
                elif self.gui.showing_scoreboard:
                    # 如果正在显示计分板，点击任意位置返回游戏
                    self.gui.showing_scoreboard = False
                elif self.is_game_over:
                    if self.gui.reset_button.rect.collidepoint(event.pos):
                        self.circle += 1  # 游戏次数增加1轮
                        self.reset_game()
                    elif self.gui.score_button.rect.collidepoint(event.pos):
                        # 计算分数
                        fan, messages = self.calculate_fan(self.winner)
                        self.gui.show_scores(self.winner, self.winning_tile, messages)
                    elif self.gui.quit_button.rect.collidepoint(event.pos):
                        pygame.quit()
                        sys.exit()
                    elif self.gui.rules_button.rect.collidepoint(event.pos):
                        self.gui.show_rules()
                    elif self.gui.scoreboard_button.rect.collidepoint(event.pos):
                        self.gui.show_scoreboard()
                else:
                    # 检查各种按钮点击
                    if self.gui.rules_button.rect.collidepoint(event.pos):
                        self.gui.show_rules()
                    elif self.gui.scoreboard_button.rect.collidepoint(event.pos):
                        self.gui.show_scoreboard()
                    elif self.gui.knowledge_bubble:
                        # 如果有知识气泡显示，点击清除
                        self.gui.clear_knowledge_bubble()
                    else:
                        self.handle_mouse_button_down()
        return True

    # 在 handle_mouse_button_down 方法中，通过 pygame.mouse.get_pos() 获取到了鼠标的位置mouse_pos。
    # 然后，方法进入了一个循环，遍历了所有的玩家，找到了当前玩家的手牌，然后检查鼠标是否在这些手牌的区域内点击了下去。 在内部循环中，x 和 y
    # 变量被初始化为手牌的起始坐标，然后逐个将每张手牌的矩形区域向右移动，并检查鼠标点击位置是否在这个矩形区域内。这个 x 和 y 的作用就是用来确定每张手牌的位置的偏移量。

    def update(self):
        # 如果正在显示规则或计分板，不更新游戏状态
        if self.gui.showing_rules or self.gui.showing_scoreboard:
            return
            
        # 更新知识气泡状态
        self.gui.update_knowledge_bubble()
            
        # 更新游戏状态
        if not self.is_game_over:
            # 在每次循环迭代时更新游戏状态并渲染新的帧
            self.gui.render_game_state(self.dealer, self.is_game_over)
            self.determine_and_display_discarded_tile()

    def render(self):
        # 如果正在显示规则或计分板，不需要额外渲染
        if self.gui.showing_rules or self.gui.showing_scoreboard:
            return
            
        # 只有在游戏未结束时才渲染按钮
        if self.is_game_over:
            self.draw_end()
            # 游戏结束时显示重新开始按钮
            mouse_pos = pygame.mouse.get_pos()
            self.gui.reset_button.draw(self.gui.screen, mouse_pos)
            if self.gui.score_button_drawn:
                self.gui.score_button.draw(self.gui.screen, mouse_pos)
        else:
            # 显示游戏状态信息
            self.print_game_state()
        pygame.display.flip()


# 主函数
def main():
    # 创建玩家 - 使用著名密码学学者替代Bot
    player1 = CryptoScholar("图灵", "东")  # 位置 东 表示左方玩家 - 阿兰·图灵
    player2 = Player("玩家", "南")  # 位置 南 表示下方玩家 - 人类玩家
    player3 = CryptoScholar("香农", "西")  # 位置 西 表示右方玩家 - 克劳德·香农
    player4 = CryptoScholar("柯克霍夫", "北")  # 位置 北 表示上方玩家 - 奥古斯特·柯克霍夫

    # 临时注释掉日志重定向，方便调试
    # log_file = open("game.log", "w")
    # sys.stdout = log_file
    
    # 初始化游戏
    try:
        gui = GUI([player1, player2, player3, player4])
        game = Game(gui)
        
        # 显示学者介绍
        print("欢迎来到码将 - 密码学大师版！")
        for player in [player1, player3, player4]:  # 排除人类玩家
            if hasattr(player, 'get_scholar_introduction'):
                print(f"{player.name}: {player.get_scholar_introduction()}")
        
        game.start_game()

        # 游戏循环
        running = True
        while running:
            running = game.handle_events()
            if game.gui.showing_rules:
                # 如果正在显示规则，重新渲染规则页面
                game.gui.show_rules()
            elif game.gui.showing_scoreboard:
                # 如果正在显示计分板，重新渲染计分板
                game.gui.show_scoreboard()
            else:
                # 正常游戏状态
                game.update()
                game.render()
    except Exception as e:
        print(f"游戏启动出错: {e}")
        import traceback
        traceback.print_exc()
    finally:
        # 退出 Pygame
        pygame.quit()
        sys.exit()


if __name__ == "__main__":
    main()

# main() --> 初始化玩家和GUI --> 开始游戏 (game.start_game())
#         |--> 游戏循环开始 (while running)
#             |--> 处理事件 (game.handle_events())
#             |     |--> 如果有退出事件，设置 running 为 False
#             |     |--> 如果有鼠标点击事件，处理玩家出牌 (game.handle_mouse_button_down())
#             |--> 更新游戏状态 (game.update())
#             |     |--> 检查游戏是否结束 (game.is_game_over)
#             |     |--> 如果游戏未结束，更新当前玩家状态 (game.determine_and_display_discarded_tile())
#             |--> 渲染游戏状态 (game.render())
#                   |--> 显示当前游戏状态 (game.print_game_state())
#                   |--> 如果游戏结束，显示流局图片 (game.draw_end())
